using System;
using System.Collections.Generic;
using System.Text;
using Mpi.Common;
using System.Diagnostics;
namespace Xeml.Visualization
{
    /// <summary>
    /// Represents a time region and it's projection to pixel measure.
    /// </summary>
    public class TimeRegion
    {
        #region Fields
        private TimeSpan _length;
        private long _pxLength;
        internal TimeRegionList _collection;
        private int _minPerPixel = 60;
        private int _logicPxSize = 1;
        
        #endregion

        #region Properties
        /// <summary>
        /// The start time of this region.
        /// </summary>
        public TimeSpan Start
        {
            get
            {
                LinkedListNode<TimeRegion> lln = this.Collection.Find(this);
                if (lln.Previous != null)
                    return lln.Previous.Value.End;
                else
                    return TimeSpan.Zero;
            }
        }

        /// <summary>
        /// The en time of this region.
        /// </summary>
        public TimeSpan End
        {
            get
            {
                return this.Start + this._length;
            }
        }




        /// <summary>
        /// The available resolution minustes per pixel.
        /// </summary>
        public int MinutesPerPixel
        {
            get { return _minPerPixel; }
            set 
            {
                int newVal = Math.Max(value, 1);
                int old = _minPerPixel;
                if (_minPerPixel != newVal)
                {
                    _minPerPixel = newVal;
                    OnResolutionChanged(old, newVal);
                }

            }
        }



        /// <summary>
        /// The number of physical pixel per logical pixel.
        /// </summary>
        public int LogicPixelSize
        {
            get { return _logicPxSize; }
            set { _logicPxSize = Math.Max(value, 1); }
        }

        /// <summary>
        /// The physical available pixel for this time region.
        /// </summary>
        public int PxLength
        {
            get
            {
                // return _pxLength * _logicPxSize;
                return this.EndPx - this.StartPx;

                //return Map(this.End);
            }
            set
            {
                int oldRes = _minPerPixel;
                int newPx = value / this._logicPxSize;

                _minPerPixel = ((int)_length.TotalMinutes / newPx);
                OnResolutionChanged(oldRes, _minPerPixel);

                //if (_pxLength != newPx && newPx <= (long)_length.TotalMinutes)
                //{
                //    _pxLength = newPx;
                //    _minPerPixel = (uint)((long)_length.TotalMinutes / _pxLength);
                //    OnResolutionChanged(oldRes, _minPerPixel);
                //}
            }
        }

        /// <summary>
        /// The collection where this region is contained.
        /// </summary>
        public TimeRegionList Collection
        {
            get { return _collection; }
            set
            {
                if (this._collection != null && this.Collection != value)
                    this._collection.Remove(this);
                this._collection = value;
                //this.collection.AddLast(this);
            }
        }


        /// <summary>
        /// The start pixel of this region.
        /// </summary>
        public int StartPx
        {
            get
            {
                if (this._collection != null)
                {
                    // return this._collection.StartPxOf(this);
                    return Map(this.Start);
                }
                return -1;
            }

        }

        /// <summary>
        /// The end pixel of this region.
        /// </summary>
        public int EndPx
        {
            get
            {
                if (this._collection != null)
                {
                    //return this.StartPx + this.PxLength;
                    return Map(this.End);
                }
                return -1;
            }

        }


        /// <summary>
        /// Pixel per hour resolution. Default is 1 px/h, that means each pixel represents 1 hour.
        /// </summary>
        //public float Resolution
        //{
        //    get { return resolution; }
        //    set
        //    {
        //        if (value <= 0)
        //            throw new ArgumentOutOfRangeException("resolution", "The resolution can't be lesser or equal zero");

        //        if (resolution != value)
        //        {
        //            float oldRes = this.resolution;
        //            resolution = value;
        //            this.pxLength = (float)this.length.TotalHours * this.resolution;
        //            OnResolutionChanged(oldRes, resolution);
        //        }
        //    }
        //}

        //public float PixelSaveResolution
        //{
        //    get
        //    {
        //        return this.pxLength / (float)this.length.TotalHours;
        //    }
        //}

        /// <summary>
        /// Get's the a zoom factor based up the resolution property.
        /// </summary>
        public int ZoomFactor
        {
            get
            {
                int ret;

                ret = (60 / (int)_minPerPixel) * 100;
                //ret = (float)Math.Round(ret, 2);
                return ret;
            }
        }


        /// <summary>
        /// The length point of the region.
        /// </summary>
        public TimeSpan Length
        {
            get { return _length; }
            set
            {
                //if (value <= start)
                //    throw new ArgumentOutOfRangeException("end", "The end point can'delayTimer be lesser or equal to the end point");
                if (_length != value && value >= TimeSpan.FromMinutes(1))
                {
                    TimeSpan old = this._length;
                    _length = value;
                    //this._pxLength = (long)this._length.TotalMinutes / _minPerPixel / _logicPxSize;
                    //OnResolutionChanged(this.resolution, this.resolution);
                    OnLengthHasChanged(old);
                }
            }
        }
        #endregion

        #region Constructors
        public TimeRegion()
        {
            //start = TimeSpan.Zero;
            _length = new TimeSpan(1, 0, 0, 0);
            _minPerPixel = 60;
            _logicPxSize = 2;
        }
        #endregion


       

        public int ToLogicPixel(int physicalPx)
        {
            int logicPx = (int)Math.Floor(((double)physicalPx / (double)_logicPxSize));
            return logicPx;
        }


        public TimeSpan Map(int physPixel)
        {
            int logicPx = ToLogicPixel(physPixel);// (long)Math.Floor(((double)physPixel / (double)_logicPxSize));
            int startLogicPx = ToLogicPixel(this.StartPx);// (long)Math.Floor(((double)this.StartPx / (double)_logicPxSize));
            int endLogicPx = ToLogicPixel(this.EndPx);// (long)Math.Floor(((double)this.EndPx / (double)_logicPxSize));
            if (logicPx < startLogicPx || logicPx > endLogicPx)
            {
                Debug.WriteLine("TimeRegion: wrong region");
                return TimeSpan.FromTicks(-1);
                
            }
            int start = (logicPx - startLogicPx) * _minPerPixel;
            //start = ;
            int end = start + _minPerPixel;
            //long minutes = start;
            for (long i = start; i < end; i++)
                if (i % 60 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 30 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 20 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 15 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 12 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 10 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 5 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);
            for (long i = start; i < end; i++)
                if (i % 2 == 0)
                    return this.Start + TimeSpan.FromMinutes(i);

            return this.Start + TimeSpan.FromMinutes(start);

        }

        public int Map(TimeSpan ts)
        {
            if (ts < this.Start || ts > this.End)
                return -1;
            decimal px = ((int)Math.Floor(ts.TotalMinutes) - (int)Math.Floor(this.Start.TotalMinutes));
            px = px / _minPerPixel;
            px = px * _logicPxSize;
            if (this.Collection != null)
            {
                LinkedListNode<TimeRegion> prev = this.Collection.Find(this).Previous;
                if (prev != null)
                    px = px + prev.Value.EndPx;
            }
            //if (ts != TimeSpan.FromDays(2) && ts < TimeSpan.FromDays(10))
            //    Debug.WriteLine("TimeRegion: Mappe " + ts.ToString() + "->" + px.ToString());
            return (int)px;
        }

        #region Events
        protected virtual void OnResolutionChanged(long oldRes, long newRes)
        {
            if (this.ResolutionChanged != null)
                ResolutionChanged(this, new ResolutionEventArgs(oldRes, newRes));
        }

        private void OnLengthHasChanged(TimeSpan old)
        {
            if (this.LengthHasChanged != null)
                LengthHasChanged(this, new PropertyHasChangedEventArgs<TimeSpan>(old, this._length));
        }

        public event System.EventHandler<ResolutionEventArgs> ResolutionChanged;
        public event EventHandler<PropertyHasChangedEventArgs<TimeSpan>> LengthHasChanged;
        #endregion

    }


}
